﻿using System;
using System.IO;
using System.IO.Packaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Xps;
using System.Windows.Xps.Packaging;
using BeeCoders.Tools.Wpf.Infrastructure;
using BeeCoders.Tools.Wpf.PdfCanvas.Converters;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Microsoft.Win32;
using Rectangle = System.Windows.Shapes.Rectangle;

namespace BeeCoders.Tools.Wpf.PdfCanvas
{
	public enum PdfPageSize
	{
		A0 = 0,
		A1 = 1,
		A2 = 2,
		A3 = 3,
		A4 = 4,
		A5 = 5
	}

	public class PdfCanvas : Canvas
	{
		private Point pdfCanvasOrigin;

		public PdfCanvas()
		{
			Initialized += PdfCanvasInitialized;
			Loaded += PdfCanvasLoaded;
		}

		void PdfCanvasLoaded(object sender, RoutedEventArgs e)
		{
			DrawRulers(false);

			// Init base data
			Window wnd = Window.GetWindow(this);
			if (wnd != null)
			{
				pdfCanvasOrigin = TransformToAncestor(wnd).Transform(new Point(0, 0));
			}


		}

		static void PdfCanvasInitialized(object sender, EventArgs e)
		{
			//		DrawRulers(false);
		}

		static RegistryKey fontsKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\Fonts");

		static public string GetFontFile(string fontName)
		{
			return fontsKey.GetValue(fontName, string.Empty) as string;
		}

		public PdfPageSize PdfPageSize
		{
			get { return (PdfPageSize)GetValue(PageSizeProperty); }
			set
			{
				SetValue(PageSizeProperty, value);

			}
		}

		public static readonly DependencyProperty PageSizeProperty = DependencyProperty.Register("PdfPageSize",
			typeof(PdfPageSize),
			typeof(PdfCanvas),
			new FrameworkPropertyMetadata(PdfPageSize.A4));

		public iTextSharp.text.Rectangle PageSize
		{
			get
			{
				switch (PdfPageSize)
				{
					case PdfPageSize.A3:
						return iTextSharp.text.PageSize.A3;
					case PdfPageSize.A4:
						return iTextSharp.text.PageSize.A4;
					case PdfPageSize.A5:
						return iTextSharp.text.PageSize.A5;

					default:
						return iTextSharp.text.PageSize.A4;
				}
			}
		}

		#region Rulers

		public bool ShowRuler
		{
			get { return (bool)GetValue(ShowRulerProperty); }
			set
			{
				SetValue(ShowRulerProperty, value);

			}
		}

		public static readonly DependencyProperty ShowRulerProperty = DependencyProperty.Register("ShowRuler", typeof(bool), typeof(PdfCanvas), new FrameworkPropertyMetadata(false, OnShowRulerChanged));

		private static void OnShowRulerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			foreach (var child in ((PdfCanvas)d).Children)
			{
				if (child is Line)
				{
					var ucCurrentChild = child as Line;

					if (ucCurrentChild != null)
					{
						if ((ucCurrentChild.Tag.ToString() == "V") || (ucCurrentChild.Tag.ToString() == "H"))
						{
							if ((bool)e.NewValue)
							{
								ucCurrentChild.Visibility = Visibility.Visible;
							}
							else
							{
								ucCurrentChild.Visibility = Visibility.Hidden;
							}
						}
					}
				}
			}
		}


		public double MajorRulerTickMark
		{
			get { return (double)GetValue(MajorRulerTickMarkProperty); }
			set
			{
				SetValue(MajorRulerTickMarkProperty, value);

			}
		}

		public static readonly DependencyProperty MajorRulerTickMarkProperty = DependencyProperty.Register("MajorRulerTickMark", typeof(double), typeof(PdfCanvas), new FrameworkPropertyMetadata((double)20.0f, OnMajorRulerTickMarkChanged));

		private static void OnMajorRulerTickMarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((PdfCanvas)d).DrawRulers(true);
		}

		public double MinorRulerTickMark
		{
			get { return (double)GetValue(MinorRulerTickMarkProperty); }
			set
			{
				SetValue(MinorRulerTickMarkProperty, value);

			}
		}

		public static readonly DependencyProperty MinorRulerTickMarkProperty = DependencyProperty.Register("MinorRulerTickMark", typeof(double), typeof(PdfCanvas), new FrameworkPropertyMetadata((double)5.0f, OnMinorRulerTickMarkChanged));

		private static void OnMinorRulerTickMarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((PdfCanvas)d).DrawRulers(true);
		}

		public void DrawRulers(bool removeLinesFirst)
		{


			if (removeLinesFirst)
			{
				int intTotalChildren = Children.Count - 1;
				for (int intCounter = intTotalChildren; intCounter > 0; intCounter--)
				{
					if (Children[intCounter] is Line)
					{
						var ucCurrentChild = Children[intCounter] as Line;

						if (ucCurrentChild != null)
						{
							if ((ucCurrentChild.Tag.ToString() == "V") || (ucCurrentChild.Tag.ToString() == "H")) Children.Remove(ucCurrentChild);
						}
					}
				}
			}

			var p = Parent as Border;

			// Create a red Brush
			var lineBrush = new SolidColorBrush();
			lineBrush.Color = Colors.Black;

			double xPos = MajorRulerTickMark;

			while (xPos < this.ActualWidth - MajorRulerTickMark / 2)
			{
				var newLine = new Line();

				newLine.Tag = "V";
				newLine.X1 = xPos;
				newLine.Y1 = 0;
				newLine.X2 = xPos;
				newLine.Y2 = 30;			// hight of marker

				newLine.SetValue(ZIndexProperty, 0);


				// Set Line's width and color
				newLine.StrokeThickness = 0.5;
				newLine.Stroke = lineBrush;
				newLine.StrokeDashArray = new DoubleCollection() { 2, 4 };

				if (ShowRuler)
				{
					newLine.Visibility = Visibility.Visible;
				}
				else
				{
					newLine.Visibility = Visibility.Hidden;
				}
				Children.Add(newLine);

				xPos += MajorRulerTickMark;
			}

			double yPos = MajorRulerTickMark;

			while (yPos < this.ActualHeight - MajorRulerTickMark / 2)
			{
				var newLine = new Line();

				newLine.Tag = "H";
				newLine.X1 = 0;
				newLine.Y1 = yPos;
				newLine.X2 = 30;
				newLine.Y2 = yPos;

				// Set Line's width and color
				newLine.StrokeThickness = 0.5;
				newLine.Stroke = lineBrush;
				newLine.StrokeDashArray = new DoubleCollection() { 2, 4 };

				if (ShowRuler)
				{
					newLine.Visibility = Visibility.Visible;
				}
				else
				{
					newLine.Visibility = Visibility.Hidden;
				}
				Children.Add(newLine);

				yPos += MajorRulerTickMark;
			}
		}
		#endregion

		public void SaveXps(string filename)
		{

			// Save current canvas transorm
			Transform transform = this.LayoutTransform;
			// Temporarily reset the layout transform before saving
			this.LayoutTransform = null;

			// Get the size of the canvas
			Size size = new Size(this.Width, this.Height);
			// Measure and arrange elements
			this.Measure(size);
			this.Arrange(new Rect(size));

			// Open new package
			Package package = Package.Open(filename, FileMode.Create);
			// Create new xps document based on the package opened
			var doc = new XpsDocument(package);
			// Create an instance of XpsDocumentWriter for the document
			XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
			// Write the canvas (as Visual) to the document
			writer.Write(this);
			// Close document
			// Close package

			// Restore previously saved layout
			this.LayoutTransform = transform;

			IXpsFixedDocumentSequenceReader docSequence = doc.FixedDocumentSequenceReader;

			foreach (IXpsFixedDocumentReader docSeqDocument in docSequence.FixedDocuments)
			{
				foreach (IXpsFixedPageReader docPage in docSeqDocument.FixedPages)
				{
					var docXml = docPage.XmlReader;

					if (docXml != null)
					{
						while (docXml.Read())
						{
							Console.WriteLine(docXml.Name);
						}
					}
				}
			}

			doc.Close();
			package.Close();



		}

		//public Point PointToWindow(UIElement element, Point pointOnElement)
		//{
		//    Window wnd = Window.GetWindow(element);
		//    if (wnd == null)
		//    {
		//        throw new InvalidOperationException("target element is not yet connected to the Window drawing surface");
		//    }
		//    Point p = element.TransformToAncestor(wnd).Transform(pointOnElement);

		//    p = new Point(p.X - pdfCanvasOrigin.X, p.Y - pdfCanvasOrigin.Y);

		//    return p;
		//}

		public Point PointToWindow(UIElement element, Point pointOnElement)
		{

			Point p = element.TransformToAncestor(this).Transform(pointOnElement);

			p = new Point(p.X, p.Y);

			return p;
		}
		public void Save(string filename)
		{
			var doc = new iTextSharp.text.Document(PageSize, 0, 0, 0, 0);

			var writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, new FileStream(filename, FileMode.Create));

			doc.Open();
			try
			{


				float docWidth = doc.Right;// -doc.Left;
				float docHeight = doc.Top;// -doc.Bottom;

				float aspectRatioX = docWidth / (float)ActualWidth;
				float aspectRatioY = docWidth / (float)ActualWidth;

				iTextSharp.text.pdf.PdfContentByte cb = writer.DirectContent;

				for (int i = 0; i < VisualTreeHelper.GetChildrenCount(this); i++)
				{
					// Retrieve child visual at specified index value.
					var childVisual = (Visual)VisualTreeHelper.GetChild(this, i);




					if (childVisual.GetType() == typeof(TextBlock))
					{
						var o = childVisual as TextBlock;


						float oLeft = float.Parse(o.GetValue(LeftProperty).ToString()) * aspectRatioX;
						float oTop = float.Parse(o.GetValue(TopProperty).ToString()) * aspectRatioY;
						float oWidth = (float)o.ActualWidth * aspectRatioX;
						float oHeight = (float)o.ActualHeight * aspectRatioY;

						oTop = docHeight - oTop;


						// we tell the ContentByte we're ready to draw text
						cb.BeginText();

						foreach (var fontwpf in Fonts.SystemFontFamilies)
						{
							Console.WriteLine(fontwpf.Source);
						}

						foreach (string fontname in FontFactory.RegisteredFonts)
						{
							Console.WriteLine(fontname);
						}

						var fileN = GetFontFile(o.FontFamily.Source + " (TrueType)");

						Font f = FontFactory.GetFont("Arial", 12);

						var font = BaseFont.CreateFont(@"c:\windows\fonts\" + fileN, BaseFont.CP1252, BaseFont.EMBEDDED);
						//	BaseFont font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

						// we draw some text on a certain position
						cb.SetTextMatrix(oLeft, oTop);
						cb.SetFontAndSize(font, (float)o.FontSize);
						cb.ShowText(o.Text);

						// we tell the contentByte, we've finished drawing text
						cb.EndText();
					}

					if (childVisual.GetType() == typeof(Rectangle))
					{
						var o = childVisual as Rectangle;

						float oLeft = float.Parse(o.GetValue(LeftProperty).ToString()) * aspectRatioX;
						float oTop = float.Parse(o.GetValue(TopProperty).ToString()) * aspectRatioY;
						float oWidth = (float)o.ActualWidth * aspectRatioX;
						float oHeight = (float)o.ActualHeight * aspectRatioY;

						oTop = docHeight - oTop;
						cb.Rectangle(oLeft, oTop, oWidth, -oHeight);

						cb.SetCMYKColorFill(50, 20, 120, 15);
						cb.SetCMYKColorStroke(120, 100, 80, 150);
						cb.Stroke();
					}

					if (childVisual.GetType() == typeof(Ellipse))
					{
						var o = childVisual as Ellipse;



						float oLeft = float.Parse(o.GetValue(LeftProperty).ToString()) * aspectRatioX;
						float oTop = float.Parse(o.GetValue(TopProperty).ToString()) * aspectRatioY;
						float oWidth = (float)o.ActualWidth * aspectRatioX;
						float oHeight = (float)o.ActualHeight * aspectRatioY;

						oTop = docHeight - oTop;
						cb.Ellipse(oLeft, oTop, oLeft + oWidth, oTop - oHeight);
						cb.SetCMYKColorFill(50, 20, 120, 15);
						cb.SetCMYKColorStroke(120, 100, 80, 150);
						cb.Stroke();


						//var bitmap = new RenderTargetBitmap((int)ellipse.ActualWidth, (int)ellipse.ActualHeight, 96, 96, PixelFormats.Pbgra32);
						//bitmap.Render(ellipse);
						//bitmap.Freeze();

						//var encoder = new PngBitmapEncoder();
						//// Choose type here ie: JpegBitmapEncoder, etc
						////BitmapEncoder encoder = new PngBitmapEncoder();  // Choose type here ie: JpegBitmapEncoder, etc
						//encoder.Frames.Add(BitmapFrame.Create(bitmap));

						//var ms = new MemoryStream();
						//encoder.Save(ms);

						//iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(ms.ToArray());

						//FileStream fs = File.OpenWrite(@"d:\blah2.png");

						//ms.WriteTo(fs);

						///* could replace with GetBuffer() if you don't mind the padding, or you
						//could set Capacity of ms to Position to crop the padding out of the
						//buffer.*/
						////fs.Write(ms.GetBuffer(),0,(int)ms.Position);

						//fs.Close();


						//		gif.ScaleToFit(200, 200);

						//		iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(@"d:\test.png");

						//	doc.Add(gif);
					}

					//    if (childVisual.GetType() == typeof(XamCode128Barcode))
					//    {
					//        var bc = childVisual as XamCode128Barcode;


					//        var bitmap = new RenderTargetBitmap((int)bc.ActualWidth,(int)bc.ActualHeight,  96, 96,PixelFormats.Pbgra32);
					//        bitmap.Render(childVisual);
					//        bitmap.Freeze();

					//        var encoder = new PngBitmapEncoder();
					//        // Choose type here ie: JpegBitmapEncoder, etc
					//        //BitmapEncoder encoder = new PngBitmapEncoder();  // Choose type here ie: JpegBitmapEncoder, etc
					//        encoder.Frames.Add(BitmapFrame.Create(bitmap));

					//        var ms = new MemoryStream();
					//        encoder.Save(ms);


					//        FileStream fs = File.OpenWrite(@"d:\blah.png");

					//        ms.WriteTo(fs);

					//        /* could replace with GetBuffer() if you don't mind the padding, or you
					//        could set Capacity of ms to Position to crop the padding out of the
					//        buffer.*/
					//        //fs.Write(ms.GetBuffer(),0,(int)ms.Position);

					//        fs.Close();
					//        Image gif = Image.GetInstance(ms.ToArray());


					////		iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(@"d:\test.png");

					//        doc.Add(gif);

					//    }
					// Do processing of the child visual object.

					// Enumerate children of the child visual object.
					//	EnumVisual(childVisual);
				}

				cb.Ellipse(0, 0, 20, 20);
				cb.SetCMYKColorFill(50, 20, 120, 15);
				cb.SetCMYKColorStroke(120, 100, 80, 150);
				cb.Stroke();

				cb.Ellipse(0, docHeight, 20, docHeight - 20);
				cb.SetCMYKColorFill(50, 20, 120, 15);
				cb.SetCMYKColorStroke(120, 100, 80, 150);
				cb.Stroke();

				foreach (var child in this.Children)
				{
				}
				//cb.MoveTo(doc.PageSize.Width / 2, doc.PageSize.Height / 2);
				//cb.LineTo(doc.PageSize.Width / 2, doc.PageSize.Height);
				//cb.Stroke();
				//cb.MoveTo(0, doc.PageSize.Height / 2);
				//cb.LineTo(doc.PageSize.Width, doc.PageSize.Height / 2);
				//cb.Stroke();

				//cb.Ellipse(100f,100f,200f,200f);
				//cb.Stroke();

				doc.Close();
			}
			catch (Exception ex)
			{

				Console.WriteLine(ex.Message);
				doc.Close();
			}
		}

		public void SaveAsPdf(string filename)
		{

			//Point p = PointToPdfCanvas();

			try
			{
				var doc = new iTextSharp.text.Document(PageSize, 0, 0, 0, 0);

				var writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, new FileStream(filename, FileMode.Create));

				doc.Open();

				float docWidth = doc.Right;// -doc.Left;
				float docHeight = doc.Top;// -doc.Bottom;

				float aspectRatioX = docWidth / (float)ActualWidth;
				float aspectRatioY = docWidth / (float)ActualWidth;

				PdfContentByte cb = writer.DirectContent;

				BuildPdf(cb, this, new Point(docWidth, docHeight), new Point(aspectRatioX, aspectRatioY));

				doc.Close();
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);

			}


		}

		private void BuildPdf(PdfContentByte cb, Visual v, Point documentSize, Point aspectRatio)
		{
			Console.WriteLine(@"BuildPdf, AspectRatio: {0} {1}", aspectRatio.X, aspectRatio.Y);

			string name = null;
			var p = new Point();

			if (v is FrameworkElement)
			{

				name = ((FrameworkElement)v).Name;
				p = PointToWindow((FrameworkElement)v, new Point(0, 0));

				if (v is Line)
				{
					BuildLine(cb, (Line)v, documentSize, aspectRatio);
				}

				if (v is Rectangle)
				{
					new RectangleConverter().Convert(this, cb, (Rectangle)v, pdfCanvasOrigin, documentSize, aspectRatio);
				}

				if (v is Ellipse)
				{
					BuildEllipse(cb, (Ellipse)v, documentSize, aspectRatio);
				}

				if (v is TextBlock)
				{
					BuildTextBlock(cb, (TextBlock)v, documentSize, aspectRatio);
				}
			}




			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(v); i++)
			{
				BuildPdf(cb, VisualTreeHelper.GetChild(v, i) as Visual, documentSize, aspectRatio);
			}

		}

		private void BuildLine(PdfContentByte cb, Line o, Point documentSize, Point aspectRatio)
		{
			Point p1 = PointToWindow(o, new Point(o.X1, o.Y1));
			Point p2 = PointToWindow(o, new Point(o.X2, o.Y2));


			p1 = new Point(p1.X * aspectRatio.X, p1.Y * aspectRatio.Y);
			p2 = new Point(p2.X * aspectRatio.X, p2.Y * aspectRatio.Y);



			float oWidth = (float)o.ActualWidth * (float)aspectRatio.X;
			float oHeight = (float)o.ActualHeight * (float)aspectRatio.Y;

			cb.MoveTo((float)p1.X, (float)documentSize.Y - (float)p1.Y);
			cb.LineTo((float)p2.X, (float)documentSize.Y - (float)p2.Y);

			cb.SetCMYKColorFill(50, 20, 120, 15);
			cb.SetCMYKColorStroke(120, 100, 80, 150);
			cb.Stroke();
		}

		//private void BuildRectangle(PdfContentByte cb, Rectangle o, Point origin, Point documentSize, Point aspectRatio)
		//{
		//    Point p = PointToWindow(o, new Point(0,0));

		//    p = new Point(p.X * aspectRatio.X, p.Y*aspectRatio.Y);

		//    float oWidth = (float)o.ActualWidth * (float)aspectRatio.X;
		//    float oHeight = (float)o.ActualHeight * (float)aspectRatio.Y;

		//    Brush s = o.Stroke;

		//    if (s is SolidColorBrush)
		//    {
		//        Color c = ((SolidColorBrush) s).Color;
		//        cb.SetRGBColorStroke(c.R,c.G,c.B);
		//    }
		//    else
		//    {
		//    cb.SetColorStroke(BaseColor.BLUE);


		//    }
		//    cb.Rectangle((float)p.X,  (float)documentSize.Y - (float)p.Y, oWidth, -oHeight);


		//    cb.SetCMYKColorFill(50, 20, 120, 15);
		////	cb.SetCMYKColorStroke(120, 100, 80, 150);
		//    cb.Stroke();
		//}

		private void BuildEllipse(PdfContentByte cb, Ellipse o, Point documentSize, Point aspectRatio)
		{
			Point p = PointToWindow(o, new Point(0, 0));

			p = new Point(p.X * aspectRatio.X, p.Y * aspectRatio.Y);



			float oWidth = (float)o.ActualWidth * (float)aspectRatio.X;
			float oHeight = (float)o.ActualHeight * (float)aspectRatio.Y;


			cb.Ellipse((float)p.X, (float)documentSize.Y - (float)p.Y, (float)p.X + oWidth, (float)documentSize.Y - (float)p.Y - oHeight);

			cb.SetCMYKColorFill(50, 20, 120, 15);
			cb.SetCMYKColorStroke(120, 100, 80, 150);
			cb.Stroke();



		}

		private void BuildTextBlock(PdfContentByte cb, TextBlock o, Point documentSize, Point aspectRatio)
		{
			Point p = PointToWindow(o, new Point(0, 0));

			p = new Point(p.X * aspectRatio.X, p.Y * aspectRatio.Y);




			// we tell the ContentByte we're ready to draw text
			cb.BeginText();

			//foreach (var fontwpf in Fonts.SystemFontFamilies)
			//{
			//    Console.WriteLine(fontwpf.Source);
			//}

			//foreach (string fontname in FontFactory.RegisteredFonts)
			//{
			//    Console.WriteLine(fontname);
			//}

			var fileN = GetFontFile(o.FontFamily.Source + " (TrueType)");

			Font f = FontFactory.GetFont("Arial", 12);

			var font = BaseFont.CreateFont(@"c:\windows\fonts\" + fileN, BaseFont.CP1252, BaseFont.EMBEDDED);
			//	BaseFont font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

			// we draw some text on a certain position
			cb.SetTextMatrix((float)p.X, (float)documentSize.Y - (float)p.Y);
			cb.SetFontAndSize(font, (float)o.FontSize);
			cb.ShowText(o.Text);

			// we tell the contentByte, we've finished drawing text
			cb.EndText();
		}
	}
}
